package info.hexin.aop3.test;

import info.hexin.aop3.AopInjectClass;
import info.hexin.aop3.AsmClassLoader;
import info.hexin.aop3.ClassAdapter;
import info.hexin.aop3.Foo;

import java.lang.reflect.Method;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class ClassWriterAopExample {

    public static void main(String[] args) throws Exception {

        AsmClassLoader asmClassLoader = new AsmClassLoader();

        Thread.currentThread().setContextClassLoader(asmClassLoader);
        Class.forName(AopInjectClass.class.getName());

        ClassReader cr = new ClassReader(Foo.class.getName());
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        cr.accept(new ClassAdapter(Opcodes.ASM4, cw), 0);

        // 到这里，如果调用cr.toByteArray，生成的字节码中，已经没有execute方法了，而是execute$1
        FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE, "test111", "Linfo/hexin/aop3/AopInjectClass1;", null, null);
        fv.visitEnd();

        // 我们接着需要增加一个execute方法
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "execute", "()V", null, null);
        // 开始增加代码
        mv.visitCode();
        /// 赋值
//        mv.visitVarInsn(Opcodes.ALOAD, 0);  
//        mv.visitVarInsn(Opcodes.ALOAD, 1); 
        mv.visitTypeInsn(Opcodes.NEW, "info/hexin/aop3/AopInjectClass1");  
        mv.visitInsn(Opcodes.DUP);  
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "info/hexin/aop3/AopInjectClass1", "<init>", "()V");  
//        mv.visitVarInsn(Opcodes.ALOAD, 2); 
//        mv.visitFieldInsn(Opcodes.GETFIELD,"info/hexin/aop3/AopInjectClass1","test111","Linfo/hexin/aop3/AopInjectClass1;");
        
        // 接下来，我们需要把新的execute方法的内容，增加到这个方法中
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitLdcInsn("Before execute");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        // 调用静态方法
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        
        mv.visitLdcInsn("End execute");
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(0, 0); // 这个地方，最大的操作数栈和最大的本地变量的空间，是自动计算的，是因为构造ClassWriter的时候使用了ClassWriter.COMPUTE_MAXS
        mv.visitEnd();
        // 到这里，就完成了execute方法的添加。
        // 可以把字节码写入到文件，用javap -c，来看下具体的内容

        Thread.currentThread().setContextClassLoader(asmClassLoader);
        System.out.println("xxxxxxxxxxxxxxxxxxxxxxxx");
        Class<?> c = asmClassLoader.defineClassFromClassFile("info.hexin.aop3.Foo$1", cw.toByteArray());

        printmethod(c);

        Object instances = c.newInstance();
        Method m = c.getMethod("execute", null);
        m.invoke(instances, null);
        //
        // System.out.println("execute$1");
        //
        // Method m1 = c.getMethod("execute$1", null);
        // m1.invoke(instances, null);

    }

    private static void printmethod(Class<?> c) {
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}
